package com.yyl.rbacs.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yyl.rbacs.sys.entity.SysMenu;
import com.yyl.rbacs.sys.entity.SysRoleMenu;
import com.yyl.rbacs.sys.entity.SysUserRole;
import com.yyl.rbacs.sys.mapper.*;
import com.yyl.rbacs.sys.service.ISysMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yyl.rbacs.sys.service.ISysRoleMenuService;
import com.yyl.rbacs.sys.service.ISysUserRoleService;
import com.yyl.rbacs.utils.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单管理 服务实现类
 * </p>
 *
 * @author YuanSir
 * @since 2022-10-28
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
    
    @Autowired
    private SysUserRoleMapper userRoleMapper;
    
    @Autowired
    private ISysRoleMenuService roleMenuService;
    
    @Autowired
    private ISysUserRoleService userRoleService;
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    
    
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<SysMenu> getMenuList(Long userId) {
        
        //定义缓存中的key
        String key = "sys:menu:getMenuList:" + userId;
        
        //0. 去缓存中查找是否有该userId对应的菜单数据，如果有直接返回
        List cacheData = redisTemplate.opsForList().range(key, 0, -1);
        if (cacheData != null && cacheData.size() > 0){ //缓存命中
            
            return cacheData;
            
        }
    
        //1. 如果登录用户是管理员的话，那么可以看到所有菜单
        if (userId.equals(Constants.SUPER_ADMIN_ID)){
    
            List<SysMenu> allMenuList = list();
            
            List<SysMenu> menuList = organizeMenu(Constants.TOP_MENU_PARENT_ID, allMenuList);
            
            //1.5 把超级管理员用户菜单写入缓存中
            redisTemplate.opsForList().rightPushAll(key, menuList);
            
            return menuList;
    
        } else {
    
            //2. 如果非管理员用户，只能看到自己拥有角色对应的菜单
            List<SysMenu> allMenuList = queryAllMenuByUserId(userId);
    
            List<SysMenu> menuList = organizeMenu(Constants.TOP_MENU_PARENT_ID, allMenuList);
            
            //2.5 把普通用户写入缓存中
            redisTemplate.opsForList().rightPushAll(key, menuList);
    
            return menuList;
            
        }
        
    }
    
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<SysMenu> queryAllMenuByUserId(Long userId) {
    
        //1. 查询当前用户的id所对应的角色id
        
        List<Long> roleIds = userRoleMapper.selectObjs(
                new LambdaQueryWrapper<SysUserRole>()
                        .eq(SysUserRole::getUserId, userId)
                        .select(SysUserRole::getRoleId))
                .stream()
                .map(o -> (Long) o)
                .collect(Collectors.toList());
        
        //2. 查询sys_role_menu表，把roleIdList的所有menu_id查询出来
        List<Object> menuIdList = roleMenuService.listObjs(new LambdaQueryWrapper<SysRoleMenu>().select(SysRoleMenu::getMenuId).in(SysRoleMenu::getRoleId, roleIds));
    
        return list(new LambdaQueryWrapper<SysMenu>().in(SysMenu::getMenuId, menuIdList));
    
    }
    
    @Override
    public Set<String> queryAdminPermSet() {
        List<SysMenu> menuList = list();
    
        //List<SysMenu> ——> List<权限字符串>
        List<String> permList = menuList.stream()
                .map(SysMenu::getPerms)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    
        // 处理出具库字段中的权限字符串之间的逗号
        HashSet<String> permsSet = new HashSet<>();
    
        for (String pers : permList) {
        
            if (pers.contains(",")) {
            
                String[] split = pers.split(",");
            
                permsSet.addAll(Arrays.asList(split));
            
            } else {
                
                permsSet.add(pers);
                
            }
        
        }
        
        return permsSet;
    
    }
    
    @Override
    public List<String> queryPermList(Long userId) {
        //根据用户id查询用户角色id
    
        List<Object> roleIdList = userRoleService.listObjs(
                new LambdaQueryWrapper<SysUserRole>()
                        .select(SysUserRole::getRoleId)
                        .eq(SysUserRole::getUserId, userId));
    
        // 查询 roleIdList 对应的 menuIdList
        List<Object> menuIdList = roleMenuService.listObjs(
                new LambdaQueryWrapper<SysRoleMenu>()
                        .select(SysRoleMenu::getMenuId)
                        .in(SysRoleMenu::getRoleId, roleIdList));
    
        // 查询menuIdList对应的所有菜单信息 List<权限字符串>
        List<Object> permsList = listObjs(
                new LambdaQueryWrapper<SysMenu>()
                        .select(SysMenu::getPerms)
                        .in(SysMenu::getMenuId, menuIdList));
    
        //去重并转为String类型的List
        List<String> permsListNoRepeat = permsList
                .stream()
                .map(Object::toString)
                .flatMap(s -> Arrays.stream(s.split(",")))
                .distinct()
                .collect(Collectors.toList());
        
        return permsListNoRepeat;
    }
    
    /**
     * 把当前用户的所有菜单，组织成层级结构
     * @param topMenuParentId
     * @param allMenuList
     * @return
     */
    private List<SysMenu> organizeMenu(Long topMenuParentId, List<SysMenu> allMenuList) {
        
        //1. 从全部菜单中获取topMenuParentId对应的菜单list1
        List<SysMenu> list1 = allMenuList.stream()
                .filter(sysMenu -> sysMenu.getParentId().equals(topMenuParentId))
                .collect(Collectors.toList());
    
        //2. 填充菜单populateChildMenu，设计这个方法实现递归调用
        populateChildMenu(list1, allMenuList);
        
        return list1;
        
        
    }
    
    private void populateChildMenu(List<SysMenu> list1, List<SysMenu> allMenuList) {
        
        //1. 遍历list1，对于每一个菜单查找该菜单下是否有子菜单，如果有继续递归调用populateChildMenu，为所有子菜单继续填充下级菜单
    
        for (SysMenu sysMenu : list1) {
            
            // 查找当前菜单的下级菜单，返回childList
            List<SysMenu> childList = findChildMenu(sysMenu, allMenuList);
    
            if (childList.size() > 0) { // 有子菜单
                
                //为所有子菜单填充下级菜单
                populateChildMenu(childList, allMenuList);
                
                //挂载子菜单到当前菜单
                sysMenu.setList(childList);
            }
            
        }
        
    }
    
    /**
     * 查找当前菜单的子菜单
     * @param sysMenu 当前菜单
     * @param allMenuList 子菜单
     * @return
     */
    private List<SysMenu> findChildMenu(SysMenu sysMenu, List<SysMenu> allMenuList) {
    
        List<SysMenu> collect = allMenuList.stream()
                //过滤子菜单
                .filter(menu -> sysMenu.getMenuId().equals(menu.getParentId()))
                .collect(Collectors.toList());
        
        return collect;
    
    }
    
    
}
